package shun.demo.remote.display.client;

import android.os.HandlerThread;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import shun.demo.remote.display.model.Point;
import shun.demo.remote.display.model.TimeSync;
import shun.demo.remote.display.server.Config;
import shun.demo.utils.DeviceUtil;

import java.io.*;
import java.net.Socket;

/**
 * Created by chen on 14-4-25.
 */
public class XScreen {

    public static final String LOG_TAG = "client";

    private SendHandler mSendHandler;
    private HandlerThread mSendThread;
    private ReceiveThread mReceiveThread;
    private Socket mSocket;
    private ObjectOutputStream mObjectOutputStream;
    private ObjectInputStream mObjectInputStream;

    private long mTimeOffset;
    private long mLatency;
    private int mScreenLeft;
    private boolean mTimeOffsetSync;
    private boolean mScreenLeftSync;
    private Object mSyncLock = new Object();

    private static XScreen sInstance;

    public static XScreen get() {
        if (sInstance == null) {
            synchronized (XScreen.class) {
                if (sInstance == null) {
                    sInstance = new XScreen();
                }
            }
        }
        return sInstance;
    }

    private XScreen() {
    }

    private void startSendThread() {
        if (mSendThread == null) {
            synchronized (this) {
                if (mSendThread == null) {
                    mSendThread = new HandlerThread("Client-SendThread");
                    mSendThread.start();
                    mSendHandler = new SendHandler(mSendThread.getLooper(), mObjectOutputStream);
                }
            }
        }
    }

    private void startReceiveThread() {
        if (mReceiveThread == null) {
            synchronized (this) {
                if (mReceiveThread == null) {
                    mReceiveThread = new ReceiveThread(mObjectInputStream);
                    mReceiveThread.start();
                }
            }
        }
    }

    public void connect(String address) throws IOException {
        Log.d("SendHandler", "address: " + address);
        mSocket = new Socket(address, Config.PORT);
        mObjectOutputStream = new ObjectOutputStream(mSocket.getOutputStream());
        mObjectInputStream = new ObjectInputStream(mSocket.getInputStream());
        startSendThread();
        startReceiveThread();
        startSyncTime();
        startSyncScreen();

        waitForSync();
    }

    private void waitForSync() {
        while (!mTimeOffsetSync || !mScreenLeftSync) {
            synchronized (mSyncLock) {
                try {
                    mSyncLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void startSyncTime() {
        TimeSync timeSync = new TimeSync();
        timeSync.setT0(SystemClock.uptimeMillis());
        send(timeSync);
    }

    private void startSyncScreen() {
        Point point = new Point(DeviceUtil.getDeviceWidth(), DeviceUtil.getDeviceHeight());
        send(point);
    }

    public void send(Object object) {
        Message message = Message.obtain();
        message.what = SendHandler.MESSAGE_SEND;
        message.obj = object;
        mSendHandler.sendMessage(message);
    }

    public int getScreenLeft() {
        return mScreenLeft;
    }

    public void setScreenLeft(int screenLeft) {
        mScreenLeft = screenLeft;
    }

    public long getTimeOffset() {
        return mTimeOffset;
    }

    public void setTimeOffset(long timeOffset) {
        mTimeOffset = timeOffset;
    }

    public long getLatency() {
        return mLatency;
    }

    public void setLatency(long latency) {
        mLatency = latency;
    }

    public boolean isScreenLeftSync() {
        return mScreenLeftSync;
    }

    public void setScreenLeftSync(boolean screenLeftSync) {
        mScreenLeftSync = screenLeftSync;
        if (screenLeftSync) {
            notifySync();
        }
    }

    public boolean isTimeOffsetSync() {
        return mTimeOffsetSync;
    }

    public void setTimeOffsetSync(boolean timeOffsetSync) {
        mTimeOffsetSync = timeOffsetSync;
        if (timeOffsetSync) {
            notifySync();
        }
    }

    private void notifySync() {
        synchronized (mSyncLock) {
            mSyncLock.notify();
        }
    }
}
